#include <stdlib.h>
#include "Arbre.h"
#include <iostream>
#include "Symbole.h"
#include "SymboleValue.h"
#include "Exception.h"


////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSeqInst::getValeur() {
    int valeur = 0;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudAffectation::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudSi
////////////////////////////////////////////////////////////////////////////////

NoeudSi::NoeudSi() : tabexp(), tabseq() {
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSi::getValeur() {
    unsigned int valeur = 0;
    unsigned int i=0;
    while (i < tabexp.size() && tabexp[i]->getValeur() != 1)
        i++;

    if (i < tabexp.size()) {
            valeur = tabseq[i]->getValeur();
        } else if (tabexp.size() < tabseq.size()) {
        valeur = tabseq[i]->getValeur();
    } else {
        valeur = 0;
    }
   
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    unsigned int i;
    cout << "Noeud - Sequence si de  " << tabseq.size() << " instruction(s)" << endl;
    for (i = 0; i < tabexp.size(); i++) {
        tabexp[i]->afficher(indentation + 1); // on affiche les conditions
        tabseq[i]->afficher(indentation + 2); // on affiche les sequences
    }
    if (i < tabseq.size()) {
        Noeud::afficher(indentation);
        cout << " instruction sinon" << endl;
        tabseq[i]->afficher(indentation + 2); // on affiche les sequences
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSi::ajouteSi(Noeud*exp, Noeud* seq) {
    tabexp.push_back(exp);
    tabseq.push_back(seq);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSi::ajouteSinon(Noeud* seq) {
    tabseq.push_back(seq);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudSwitch
////////////////////////////////////////////////////////////////////////////////

NoeudSwitch::NoeudSwitch(Noeud* var) : tabint(), tabseq() {
    variable = var;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSwitch::getValeur() {
    unsigned int valeur = 0;
    unsigned int i=0;
    while (i < tabint.size() && tabint[i]->getValeur() != variable->getValeur())
        i++;

    if (i < tabint.size()) {
            valeur = tabseq[i]->getValeur();
    } else {
        valeur = 0;
    }
   
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSwitch::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    unsigned int i;
    cout << "Noeud - Sequence switch de  " << tabseq.size() << " instruction(s)" << endl;
    for (i = 0; i < tabint.size(); i++) {
        tabint[i]->afficher(indentation + 1); // on affiche les conditions
        tabseq[i]->afficher(indentation + 2); // on affiche les sequences
    }

}

////////////////////////////////////////////////////////////////////////////////

void NoeudSwitch::ajouteCase(Noeud*inti, Noeud* seq) {
    tabint.push_back(inti);
    tabseq.push_back(seq);
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* exp, Noeud* si) {
    this->expression = exp;
    this->seqInst = si;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstTq::getValeur() {
    int valeur = 0;
    while (expression->getValeur() == 1) {
        valeur = seqInst->getValeur(); // on évalue l'expression

    }
    return valeur; // par convention, un tantque a la valeur de sa seqinst
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstTq::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - tantque" << endl;
    expression->afficher(indentation + 1); // on affiche  expression et seqinst
    seqInst->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* var) {
    this->variable = var;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstLire::getValeur() {

    int valeur; // on évalue l'expression
    cout << "veuillez saisir la valeur pour " << ((SymboleValue*) variable)->getChaine() << endl;
    cin >> valeur;
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, un lire a la valeur de sa variable
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Lire" << endl;
    variable->afficher(indentation + 1); // on affiche variable 
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* exp) {
    this->expression = exp;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstEcrire::getValeur() {

    int valeur = expression->getValeur(); // on évalue l'expression
    cout << valeur;
    return valeur; // par convention, un ecrire a la valeur de son expression
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Ecrire" << endl;
    expression->afficher(indentation + 1); // on affiche  expression
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire_ligne
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire_ligne::NoeudInstEcrire_ligne(Noeud* exp) {
    this->expression = exp;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstEcrire_ligne::getValeur() {

    int valeur = expression->getValeur(); // on évalue l'expression
    cout << valeur << endl;
    return valeur; // par convention, un ecrire a la valeur de son expression
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire_ligne::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Ecrire Ligne" << endl;
    expression->afficher(indentation + 1); // on affiche  expression
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* exp, Noeud* si) {
    this->SeqInst = si;
    this->expression = exp;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstRepeter::getValeur() {
    int valeur = 0;
    do {
        valeur = SeqInst->getValeur(); // on évalue l'expression
    } while (expression->getValeur() == 1);
    return valeur; // par convention, un tantque a la valeur de sa seqinst
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - repeter" << endl;
    expression->afficher(indentation + 1); // on affiche  expression et seqinst
    SeqInst->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* var, Noeud* exp1, Noeud* exp2, Noeud* si) {
    this->expression1 = exp1;
    this->expression2 = exp2;
    this->variable = var;
    this->SeqInst = si;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstPour::getValeur() {
    int valeur = 0;
    for (((SymboleValue*) variable)->setValeur(expression1->getValeur());
            variable->getValeur() <= expression2->getValeur();
            ((SymboleValue*) variable)->setValeur(variable->getValeur() + 1)) {
        valeur = SeqInst->getValeur(); // on évalue l'expression
    }
    return valeur; // par convention, un pour a la valeur de sa seqinst
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - pour" << endl;
    variable->afficher(indentation + 1); // on affiche variable
    expression1->afficher(indentation + 1); // on affiche  expression1
    expression2->afficher(indentation + 1); // on affiche  expression2 et seqInst
    SeqInst->afficher(indentation + 1); // en augmentant l'indentation 
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurOu
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurOu::NoeudOperateurOu(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurOu::getValeur() {
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();

    return ((og != 0) || (od != 0));
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurOu::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurEt
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurEt::NoeudOperateurEt(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurEt::getValeur() {
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();

    return ((og != 0) && (od != 0));
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurEt::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBool
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBool::NoeudOperateurBool(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBool::getValeur() {
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();

    if (operateur.operator ==("=="))
        return (og == od);
    else if (operateur.operator ==("!="))
        return (og != od);
    else if (operateur.operator ==("<="))
        return (og <= od);
    else if (operateur.operator ==(">="))
        return (og >= od);
    else if (operateur.operator ==("<"))
        return (og < od);
    else // ">"
        return (og > od);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBool::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurAdd
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurAdd::NoeudOperateurAdd(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurAdd::getValeur() {
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();

    if (operateur.operator ==("+"))
        return (od + og);
    else // "-"
        return (od - og);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurAdd::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurMult
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurMult::NoeudOperateurMult(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurMult::getValeur() {
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    try {
    if (operateur.operator ==("*"))
        return (od * og);
    else if (operateur.operator ==("/") && od != 0)
        return (og / od);
    else{
        div0 e;
        throw e;
    }}
    catch (div0 e){
       cout <<  e.what()<< endl;
       return og;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurMult::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurUnaire::getValeur() {
    int od = operandeDroit->getValeur();
    return -od;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurUnaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

